//---------------------------------------------------------------------
// Author: jachymko, Keith Hill
//
// Description: Class to implement the Get-PSSnapinHelp cmdlet. 
//              Generates a XML file containing all documentation data.
//
// Creation Date: Dec 23, 2006
//---------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Management.Automation;
using System.Reflection;
using System.Security;
using System.Text;
using System.Xml;

namespace PscxDeveloper.Commands
{
    [Cmdlet(VerbsCommon.Get, "PSSnapinHelp", DefaultParameterSetName = ParameterSetPath)]
    public partial class GetSnapinHelpCommand : Pscx.Commands.Internal.PscxXmlWriterCommandBase
    {
        const string NamespaceXmlSchema = "http://www.w3.org/2001/XMLSchema";
        const string NamespaceXmlSchemaInstance = "http://www.w3.org/2001/XMLSchema-instance";

        List<CmdletInfo> cmdlets;
        Visitor visitor;

        protected override void BeginProcessing()
        {        
            cmdlets = new List<CmdletInfo>();
            visitor = new Visitor(this);
            
            base.BeginProcessing();

            Output.WriteStartElement("Cmdlets");
            Output.WriteAttributeString("xmlns", "xsd", null, NamespaceXmlSchema);
            Output.WriteAttributeString("xmlns", "xsi", null, NamespaceXmlSchemaInstance);
        }

        protected override void EndProcessing()
        {
            Output.WriteEndElement();  // </Cmdlets>
            Output.WriteEndDocument();

            base.EndProcessing();
        }

        protected override void ProcessPath(string path)
        {
            Assembly assembly = null;
            try
            {
                assembly = Assembly.LoadFrom(path);
            }
            catch (Exception ex)
            {
                WriteError(new ErrorRecord(ex, "FailedAssemblyLoadFrom", ErrorCategory.InvalidArgument, path));
                return;
            }

            visitor.VisitAssembly(assembly);

            foreach (CmdletInfo cmdletInfo in cmdlets)
            {
                cmdletInfo.MergeWithLocalizedXml();

                WriteCmdlet(cmdletInfo);
            }
        }

        void WriteCmdlet(CmdletInfo cmdletInfo)
        {
            Output.WriteStartElement("Cmdlet");
            Output.WriteAttributeString("TypeName", cmdletInfo.Type.FullName);

            string description = cmdletInfo.Description;
            string detailed = cmdletInfo.DetailedDescription;

            if(string.IsNullOrEmpty(detailed)) detailed = description;

            Output.WriteElementString("Verb", cmdletInfo.Verb);
            Output.WriteElementString("Noun", cmdletInfo.Noun);
            Output.WriteElementString("Description", description);
            Output.WriteElementString("DetailedDescription", detailed);

            WriteParameterSets(cmdletInfo);
            WriteParameters(cmdletInfo);
            WriteRelatedLinks(cmdletInfo);

            Output.WriteEndElement(); // </Cmdlet>
        }

        void WriteParameterSets(CmdletInfo cmdletInfo)
        {
            Output.WriteStartElement("ParameterSets");

            if(cmdletInfo.NamedParameterSets.Count == 0)
            {
                WriteParameterSet(string.Empty, cmdletInfo.AllParametersParameterSet);
            }
            else
            {
                foreach(string setName in cmdletInfo.NamedParameterSets.Keys)
                {
                    List<ParameterInfo> currentSet = new List<ParameterInfo>();
                    currentSet.AddRange(cmdletInfo.AllParametersParameterSet);
                    currentSet.AddRange(cmdletInfo.NamedParameterSets[setName]);

                    WriteParameterSet(setName, currentSet);
                }
            }

            Output.WriteEndElement(); // </ParameterSets>

        }

        void WriteParameterSet(string name, List<ParameterInfo> parameters)
        {
            parameters.Sort(ParameterComparer);

            Output.WriteStartElement("ParameterSet");
            
            if (!string.IsNullOrEmpty(name))
            {
                Output.WriteAttributeString("Name", name);
            }

            foreach(ParameterInfo pi in parameters)
                WriteParameter(pi);

            Output.WriteEndElement();
        }

        void WriteParameter(ParameterInfo paramInfo)
        {
            paramInfo.WriteTo(Output);
        }

        void WriteParameters(CmdletInfo cmdletInfo)
        {
            Output.WriteStartElement("Parameters");

            List<ParameterInfo> all = new List<ParameterInfo>(cmdletInfo.GetAllParameters());
            all.Sort(ParameterComparer);

            foreach(ParameterInfo pi in all)
            {
                WriteParameter(pi);
            }

            Output.WriteEndElement(); // </Parameters>
        }

        void WriteRelatedLinks(CmdletInfo cmdletInfo)
        {
            Output.WriteStartElement("RelatedLinks");

            foreach(string link in cmdletInfo.RelatedLinks)
                Output.WriteElementString("Link", link);

            Output.WriteEndElement();
        }

        int ParameterComparer(ParameterInfo x, ParameterInfo y)
        {
            int xp = x.Position;
            int yp = y.Position;

            if(xp < 0 && yp < 0) return 0;

            if(yp < 0) return -1;

            if(xp < 0) return 1;

            return xp.CompareTo(yp);
        }
    }
}

